class Observer{
    constructor(data){
        this.observer(data)
    }
    observer(data){
        if(data && typeof data === 'object'){
            for(let key in data){
                this.observerNode(data,key,data[key])
            }
        }
    }
    observerNode(obj,key,value){
        this.observer(value)
        Object.defineProperty(obj,key,{
            get(){
                return value
            },
            set:(newValue) => {
                if(value!==newValue){
                    this.observer(newValue)
                    value = newValue
                }
            }
        })
    }
}
class Compile{
    constructor(el,vm){
        this.vm = vm
        this.el = this.isElementNode(el)?el:document.querySelector(el)
        let frament = this.node2Fragment(this.el)
        this.compile(frament)
        this.el.append(frament)
    }
    isElementNode(node){
        return node.nodeType === 1
    }
    node2Fragment(node){
        let frament = document.createDocumentFragment()
        let firstChild;
        while(firstChild=node.firstChild){
            frament.append(firstChild)
        }
        return frament
    }
    compile(node){
        let childNodes = node.childNodes;
        [...childNodes].forEach(item => {
            if(this.isElementNode(item)){
                this.compileNode(item) 
                this.compile(item)
            }else{
                this.textNode(item) 
            }
        })
    }
    compileNode(node){
        let attributes = node.attributes;
        [...attributes].forEach((item)=>{
            let {name,value} = item
            if(name.startsWith('v-')){
                let [,directive] = name.split('-')
                CompileUtil[directive](node,value,this.vm)
            }
        })
    }
    textNode(node){
        let content = node.textContent;
        if(/\{\{(.+?)\}\}/.test(content)){
            CompileUtil['text'](node,content,this.vm)
        }
    }
}
CompileUtil = {
    getValue(vm,value){
        window.vm = vm 
        return value.split('.').reduce((prev,current)=>{
            return prev[current]
        },vm.data)
    },
    model(node,value,vm){ 
        let valueData = this.getValue(vm,value)
        let fn = this.update['modelUpdate']
        fn(node,valueData)
    },
    text(node,content,vm){
        let fn = this.update['textUpdate']
        let contentData = content.replace(/\{\{(.+?)\}\}/,(...args)=>{
            return this.getValue(vm,args[1])
        })
        fn(node,contentData)
    },
    update: {
        modelUpdate(node,valueData){
            node.value = valueData
        },
        textUpdate(node,contentData){
            node.textContent = contentData
        }
    }
}

class Vue{
    constructor(options={}){
        this.el = options.el
        this.data = options.data
        if(this.el){
            new Observer(this.data)
            new Compile(this.el,this)
        }
    }
}